﻿/****************************** Module Header ******************************\
 * Module Name:  EncryptHelper.cs
 * Module Version:  1.0.0 (2011-12-5)
 * Copyright (c) AnyEB.Com (14507247@qq.com)
 * 
 * This class is used to encrypt string or encrypt number.
 * 
 * This source is subject to the GPL License.
 * See http://www.gnu.org/licenses/gpl.html.
 * All other rights reserved.
 * 
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
 * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace UCL
{
    /// <summary>
    /// 加密/解密助手类
    /// <para>提供加密和解密的方法</para>
    /// </summary>
    public class EncryptHelper
    {
        //默认密钥向量
        private static byte[] Keys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };

        #region StringEncrypt加密解密

        /// <summary>
        /// 加密字符串
        /// <para>密文格式:{版本}{实际文字长度}{加密后的字符串}</para>
        /// <para>注意：格式化密文后，密文长度会比原来的多出至少10个字符</para>
        /// <para>Author:天澜(14507247@qq.com) Version:2011-12-23</para>
        /// <para>伟大的微软，伟大的天澜~</para>
        /// </summary>
        /// <param name="text">要加密的单字节字符组成的字符串</param>
        /// <param name="maxOutLength">输出的最大长度</param>
        /// <param name="version">加密算法版本，最多支持10中算法(值范围 0-9)</param>
        /// <returns></returns>
        public static string StringEncrypt(string text, int maxOutLength, int version)
        {
            if (text == null || text.Length == 0) return "";
            if (version > 9 || version < 0) version = 0;
            text = string.Format("{0}:{1}", text.Length.ToString(), text);
            if (maxOutLength > 0)
            {
                int iTextLength = text.Length * 4 + 2;
                if (iTextLength > maxOutLength) return ""; // 长度超过最大长度则返回空字符串
                if (maxOutLength > iTextLength)
                {
                    double iPadLength = (maxOutLength - iTextLength) / 4;
                    iPadLength = Math.Round(iPadLength);
                    text = text.PadRight((int)iPadLength + text.Length, '0');
                }
            }

            string strDict = GetStringEncryptDictionary(version);

            Random rnd = new Random();
            ASCIIEncoding encoder = new ASCIIEncoding();

            int iItem = 0;
            string sItem = "";
            int iItema = 0;
            int iItemb = 0;
            int iRndNumbera = 0;
            int iRndNumberb = 0;
            StringBuilder sb = new StringBuilder();
            // 讲单字符加密为4个字符
            foreach (char cItem in text)
            {
                // 获取单个字符的ASCII码，范围是 32-126
                iItem = encoder.GetBytes(cItem.ToString())[0];
                // 通过算法1令字符的ASCII码限定在两位数 0-94
                iItem -= 32;
                iItema = 0; iItemb = iItem;
                if (iItem >= 10)
                {
                    sItem = iItem.ToString();
                    iItema = sItem[0].ToString().ToInt();
                    iItemb = sItem[1].ToString().ToInt();
                }
                // 对0-94数值进行补位和随机数混淆,获得4位加密字符(其中包含两位伪字符)
                iRndNumbera = rnd.Next(0, 27);
                iRndNumberb = rnd.Next(0, 27);
                sb.Append(strDict[iItema + iRndNumbera].ToString());
                sb.Append(strDict[iRndNumbera].ToString());
                sb.Append(strDict[iItemb + iRndNumberb].ToString());
                sb.Append(strDict[iRndNumberb].ToString());
            }
            sb.Insert(0, EncryptDictionaryVersion(version));
            return sb.ToString();
        }
		
        /// <summary>
        /// 加密字符串
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string StringEncrypt(string text)
        {
            return StringEncrypt(text, 0);
        }
        /// <summary>
        /// 加密字符串
        /// </summary>
        /// <param name="text"></param>
        /// <param name="maxOutLength"></param>
        /// <returns></returns>
        public static string StringEncrypt(string text, int maxOutLength)
        {
            Random rnd = new Random();
            return StringEncrypt(text, maxOutLength, rnd.Next(0, 10));
        }
        /// <summary>
        /// 判断是否是经过StringEnctypt加密过的密文
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static bool IsStringEncryptText(string text)
        {
            if (text == null || text.Length < 14) return false;
            if (((text.Length + 2) % 4) > 0) return false;
            return true;
        }
        /// <summary>
        /// 解密由StringEncrypt()加密过的密文
        /// <para>如果文本不是经过StringEncrypt()加密过的，则返回空字符串</para>
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string DeStringEncrypt(string text)
        {
            if (!IsStringEncryptText(text)) return "";
            string strDict = GetStringEncryptDictionary(DeEncryptDictionaryVersion(text.Substring(0, 2)));
            string[] arrEncText = text.Substring(2).ToArray(4);
            StringBuilder sb = new StringBuilder();
            ASCIIEncoding encoder = new ASCIIEncoding();
            int iItema, iItemb, iItem;
            foreach (string sItem in arrEncText)
            {
                iItema = strDict.IndexOf(sItem[0]) - strDict.IndexOf(sItem[1]);
                iItemb = strDict.IndexOf(sItem[2]) - strDict.IndexOf(sItem[3]);
                iItem = iItema * 10 + iItemb + 32;
                sb.Append((char)iItem);
            }
            string sText = sb.ToString();
            int iTextSplitor = sText.IndexOf(':');
            int iLength = sText.Substring(0, iTextSplitor).ToInt();
            sText = (iLength == 0) ? sText.Substring(iTextSplitor) : sText.Substring(iTextSplitor + 1, iLength);
            return sText;
        }
        #endregion

        #region NumberEncrypt加密解密

        /// <summary>
        /// 获取数字加密字典版本
        /// </summary>
        /// <param name="version"></param>
        /// <returns></returns>
        private static string GetNumberEncryptDictionary(int version)
        {
            switch (version)
            {
                case 0:
                    return "3457891206";
                case 1:
                    return "7451682093";
                case 2:
                    return "8520719634";
                case 3:
                    return "5162890437";
                case 4:
                    return "2516498370";
                case 5:
                    return "6823594701";
                case 6:
                    return "9465708312";
                case 7:
                    return "4687093125";
                case 8:
                    return "8701259346";
                case 9:
                    return "1023468597";
            }
            return "3457891206";
        }
        /// <summary>
        /// 加密数值,不限位数
        /// </summary>
        /// <param name="strNumber"></param>
        /// <returns></returns>
        public static string NumberEncrypt(string strNumber)
        {
            string strDictionary = "3568270941";
            Random rnd = new Random();
            StringBuilder sb = new StringBuilder();
            int iRndNumber = 0;

            foreach (char cItem in strNumber)
            {
                iRndNumber = rnd.Next(0, 10);
                sb.Append(GetNumberEncryptDictionary(iRndNumber)[cItem.ToString().ToInt()]);
                sb.Append(strDictionary[iRndNumber]);
            }
            return sb.ToString();
        }
        /// <summary>
        /// 解密由NumberEncrypt()加密过的密文
        /// </summary>
        /// <param name="strNumber"></param>
        /// <returns></returns>
        public static string DeNumberEncrypt(string strNumber)
        {
            string strDictionary = "3568270941";
            if (strNumber.Length % 2 > 0) strNumber = string.Format("0{0}", strNumber);
            string[] arrNumbers = strNumber.ToArray(2);
            int iRndNumber = 0;
            int iItem = 0;
            StringBuilder sb = new StringBuilder();
            foreach (string sItem in arrNumbers)
            {
                iRndNumber = strDictionary.IndexOf(sItem[1]);
                iItem = GetNumberEncryptDictionary(iRndNumber).IndexOf(sItem[0]);
                sb.Append(iItem.ToString());
            }
            return sb.ToString();
        }
        #endregion

        #region 系统自带的加密解密功能
        /// <summary>
        /// MD5加密字符串
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string MD5(string text)
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] result = md5.ComputeHash(Encoding.Default.GetBytes(text));
            return Encoding.Default.GetString(result);
        }

        /// <summary>
        /// 返回指定长度的截短后的md5加密字符串
        /// </summary>
        /// <param name="text"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static string MD5Lite(string text, int length = 6, string mixstr = "md5lite")
        {
            string md5str = MD5(text);
            md5str = MD5(md5str + mixstr);
            if (length >= md5str.Length) return md5str;
            int len = length / 2;
            return md5str.Substring(0, len) + md5str.Substring(md5str.Length - len);
        }

        /// <summary>
        /// SHA1加密字符串
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string SHA1(string text)
        {
            SHA1 sha1 = new SHA1CryptoServiceProvider();
            byte[] result = sha1.ComputeHash(Encoding.Default.GetBytes(text));
            return Encoding.Default.GetString(result);
        }
        #endregion

        #region SOMDA加密解密

        /// <summary>
        /// 获取字典版本
        /// </summary>
        /// <param name="version"></param>
        /// <returns></returns>
        private static string GetStringEncryptDictionary(int version)
        {
            switch (version)
            {
                case 0:
                    return "abcdefghijklmnopqrstuvwxyz0123456789";
                case 1:
                    return "fghiary37456o8bmnpcdejsz02tuvwlq9xk1";
                case 2:
                    return "noplmz67qrstujk89vbcde012wx5fya34ghi";
                case 3:
                    return "z01ahijwxy23456klmnobcdefgtuv7qr89ps";
                case 4:
                    return "456abcde789fghlstuvmnopxyz01qijkrw23";
                case 5:
                    return "uvwe89fghi56opqr7jklmnstxyz0123abcd4";
                case 6:
                    return "gl89hnmu45opqavw23bcstijdxyz01ef67rk";
                case 7:
                    return "9c8efg456hijmnwkldab3z07xy1opqrstuv2";
                case 8:
                    return "ef6789jghix45klmnopqyz01abcd23rstuvw";
                case 9:
                    return "mn23def5ijka67ghl4opqv18rstbcu9wxyz0";
                default:
                    return "abcdefghijklmnopqrstuvwxyz0123456789";
            }
        }

        /// <summary>
        /// 获取SOMDA字典版本
        /// </summary>
        /// <param name="version"></param>
        /// <returns></returns>
        private static string[] GetSOMDADictionary(int version)
        {
            switch (version)
            {
                case 0:
                    return new string[] { "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", "abcdefghijklmnopqrstuvwxyz", "0123456789" };
                case 1:
                    return new string[] { "fghiHIJKLMN567jkcdepwOPWXYZ01234xyzABCDqrQRSTUVstuvEFG89lmnoab", "fghijabcdpqxyzrstuvweklmno", "3456017892" };
                case 2:
                    return new string[] { "ghiHIJfK67jkcdLMN5eYZ01pwOPWX2zAB34xFG8yCDqrQRVstSTUuoabvE9lmn", "abcdpqxfgtuvweklmhisnojyzr", "5634092178" };
                case 3:
                    return new string[] { "7jgK6kcZ01pwOPWX2zAtSTUuovEnB34xFG8yCDqrQRa9lmdLMN5eYbVshiHIJf", "fghijklabcdestuvpqrzwxymno", "4056917823" };
                case 4:
                    return new string[] { "PWX2zcZ7jgK6k01pwOUuovEsh4xFG8yCDqAtSIJfnBTrQRa9lmdLMN5iH3eYbV", "ghiabcdefjkqrlmnvwxyopstuz", "6405823917" };
                case 5:
                    return new string[] { "cZ8yCgK6k01pwOUuPWX2MovEsh4xFGqAtSD7jIJfnB3eYbTrQN5iHVzRdLa9lm", "ijklabfghcdvpqrzwxymnoestu", "8640527391" };
                case 6:
                    return new string[] { "qAtSD7jIJfovEsh4xFGrnB3eYbT01pwOUuPWX2MQN5iHVzRdLa9lmcZ8yCgK6k", "bfghcijkmnoesladvxytupqrzw", "7456123890" };
                case 7:
                    return new string[] { "4xFGrnB3eYbT01qAtSD7jIJfN5iHVZ8yCgK6kzRovEshpwOUuPWX2MQdLa9lmc", "ijkmnoebfghcqrzwsytupladvx", "2305891764" };
                case 8:
                    return new string[] { "YbT01qAtSD5iHVZ8yCgK7jI4xFGrnB3eJfN6kzRUuPQdLa9lmcWovEshpwOX2M", "bfgijkmnoehcytuqrzwsdvxpla", "9301726458" };
                default:
                    return new string[] { "Z8yCtSD5iHVI9lmcWovEs4xFGrnqAdLahpwOX2MB3eJfN6kzRUuPQgK7jYbT01", "mnoehcbfgijkyrzwstuxplaqdv", "6415728930" };
            }
        }

        /// <summary>
        /// 加密版本号,生成两位版本代码<para>版本号范围：0-9</para>
        /// </summary>
        /// <param name="version"></param>
        /// <returns></returns>
        private static string EncryptDictionaryVersion(int version)
        {
            string strDict = GetStringEncryptDictionary(0);
            Random rnd = new Random();
            int iRndNumber = rnd.Next(0, 27);
            return strDict[version + iRndNumber].ToString() + strDict[iRndNumber].ToString();
        }
        /// <summary>
        /// 解密版本号
        /// </summary>
        /// <param name="version"></param>
        /// <returns></returns>
        private static int DeEncryptDictionaryVersion(string version)
        {
            string strDict = GetStringEncryptDictionary(0);
            int iRndNumber = strDict.IndexOf(version[1]);
            int iOut = strDict.IndexOf(version[0]) - iRndNumber;
            return iOut;
        }

        /// <summary>
        /// 加密字符串，加密长度为原来的3倍
        /// <para>支持英文,数字,符号加密,不支持汉字加密</para>
        /// <para>author:编程浪子; version:3.0.0 (2009-11-14)</para>
        /// <para>2013-5-14 v:3.1.0 by sutroon 合并重写方法为默认参数</para>
        /// <para>非常佩服俺自己，哈哈</para>
        /// <example>
        /// somda("_my2010password")=>ro4rp8zk7Kx4Gl5Zz1Tm4um5Ks2ql0wd8mt5wd4ks1Jx0
        /// somda("无法被加密")=>"无法被加密"
        /// </example>
        /// <![CDATA[
        /// ==TODO========================================
        /// 新增字典版本号，最大限度提高加密效果
        /// 解决偶尔加密位数错误的BUG
        /// ==UPDATE======================================
        /// 2010-08-03 by 编程浪子,重写了加密算法，强度提升N倍(N无穷大)，版本升级到2.0.0
        /// 2010-10-20 by sutroon,新增加密失败则返回原始值
        /// 2012-1-29 by sutroon,新增掩码长度和字典版本，强度更高,版本升级到3.0.0
        /// ==FLOW========================================
        /// ascii[==32~126]
        /// Fix[==288~444](256~318)
        /// POSD[==28801~44463]定位,前置和，后置随机数字典位置
        /// MOD 252 [==114073~176110]
        /// Split [==] (177-mod,left(remain,1),right(remain,1))
        /// POSD[==aa0~9y9]
        /// ]]>
        /// </summary>
        /// <param name="text"></param>
        /// <param name="maxLength"></param>
        /// <param name="version"></param>
        /// <returns></returns>
        public static string SOMDA(string text, int maxLength=0, int version=-1)
        {
            Random oRnd = new Random();
            if(version==-1) version=oRnd.Next(0, 10);
            if (text == null || text.Length == 0) return "";
            text = text.Length.ToString() + ":" + text;
            if (maxLength > 0) text = text.PadRight(maxLength / 3 - 1, '0');
            string strFixChar = "";
            string strAscChar = "";
            string strRemain = "";
            int intAscChar;
            int intMod;
            int intRnd;
            StringBuilder sb = new StringBuilder();
            // 变量定义
            string[] arrDict = GetSOMDADictionary(version);
            foreach (char c in text)
            {
                // 随机数
                intRnd = 256 + oRnd.Next(0, 62);
                // 求和定位
                strAscChar = ((byte)c + intRnd).ToString() + (318 - intRnd).ToString().PadLeft(2, '0');
                // 取模求余
                intAscChar = Int32.Parse(strAscChar);
                intMod = intAscChar / 252;
                strRemain = (intAscChar % 252).ToString().PadLeft(3, '0');
                // 定位
                strFixChar = arrDict[0][176 - intMod].ToString() + arrDict[1][Int32.Parse(strRemain.Substring(0, 2))].ToString() + arrDict[2][Int32.Parse(strRemain[2].ToString())].ToString();
                sb.Append(strFixChar);
            }
            return EncryptDictionaryVersion(version) + sb.ToString();
        }

        /// <summary>
        /// 解密由SoMDA加密的数据
        /// <para>author:编程浪子; version:2.0.1 (2009-11-14)</para>
        /// <para>狗日的，解密还真不简单。。。</para>
        /// <example>SODEMDA(SOMDA("my2020Pwd"))</example>
        /// <![CDATA[
        /// ==UPDATE====================================
        /// 2010-08-03 by 编程浪子,重写了解密算法，版本升级到2.0.0
        /// 2010-10-20 by sutroon,新增解密失败则返回原始值
        /// ]]>
        /// </summary>
        /// <param name="enctryText"></param>
        /// <returns></returns>
        public static string SODEMDA(string enctryText)
        {
            if (enctryText == null || enctryText.Length < 8) return "";
            if ((enctryText.Length - 2) % 3 > 0) return "";

            string[] arrDict = GetSOMDADictionary(DeEncryptDictionaryVersion(enctryText.Substring(0, 2)));
            enctryText = enctryText.Substring(2);
            // 变量定义 
            StringBuilder sb = new StringBuilder();
            int intMod;
            int intRemain;
            int intAscChar;
            int intRnd;

            foreach (string s in enctryText.ToArray(3))
            {
                intMod = 176 - arrDict[0].IndexOf(s[0]);
                intRemain = Int32.Parse(arrDict[1].IndexOf(s[1]).ToString() + arrDict[2].IndexOf(s[2]).ToString());
                intAscChar = intMod * 252 + intRemain;
                intRnd = Int32.Parse(intAscChar.ToString().Substring(3, 2));
                intAscChar = Int32.Parse(intAscChar.ToString().Substring(0, 3));
                intRnd = 318 - intRnd;
                intAscChar = intAscChar - intRnd;
                sb.Append((char)intAscChar);
            }
            string sOut = sb.ToString();
            int intSplit = sOut.IndexOf(':');
            if (intSplit > -1)
            {
                int intLength = Int32.Parse(sOut.Substring(0, intSplit));
                sOut = sOut.Substring(intSplit + 1, intLength);
            }
            return sOut;
        }
        #endregion

        /// <summary>
        /// DES加密字符串[2012-7-30]
        /// </summary>
        /// <param name="encryptString">待加密的字符串</param>
        /// <param name="encryptKey">加密密钥,要求为8位</param>
        /// <returns>加密成功返回加密后的字符串，失败返回源串</returns>
        public static string EncryptByDES(string s, string key = "Aa#123#_")
        {
            byte[] rgbKey = Encoding.UTF8.GetBytes(key.Substring(0, 8));
            byte[] rgbIV = Keys;
            byte[] inputByteArray = Encoding.UTF8.GetBytes(s);
            DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider();
            MemoryStream mStream = new MemoryStream();
            CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
            cStream.Write(inputByteArray, 0, inputByteArray.Length);
            cStream.FlushFinalBlock();
            return Convert.ToBase64String(mStream.ToArray());
        }

        /// <summary>
        /// DES解密[2012-7-30]
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string DeEncryptByDES(string s, string key = "Aa#123#_")
        {
            byte[] rgbKey = Encoding.UTF8.GetBytes(key);
            byte[] rgbIV = Keys;
            byte[] inputByteArray = Convert.FromBase64String(s);
            DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider();
            MemoryStream mStream = new MemoryStream();
            CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
            cStream.Write(inputByteArray, 0, inputByteArray.Length);
            cStream.FlushFinalBlock();
            return Encoding.UTF8.GetString(mStream.ToArray());

        }

        public static string Escape(string s)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in s)
            {
                sb.Append((Char.IsLetterOrDigit(c)
                    || c == '-' || c == '_' || c == '\\'
                    || c == '/' || c == '.') ? c.ToString() : Uri.HexEscape(c));
            }
            return sb.ToString();

        }
        public static string UnEscape(string s)
        {
            StringBuilder sb = new StringBuilder();
            int len = s.Length;
            int i = 0;
            while (i != len)
            {
                if (Uri.IsHexEncoding(s, i))
                    sb.Append(Uri.HexUnescape(s, ref i));
                else
                    sb.Append(s[i++]);
            }
            return sb.ToString();
        }
		
		/// <summary>
		/// 加密文本，用于自定义格式文件内容加密
		/// 如：
		/// 加密：File.SaveAllText("C:\\enc.sof",EncryptTextForFile("[test,测试内容]","sutroon"));
		/// 解密：string txt=EncryptTextForFile(File.ReadAllText("C:\\enc.sof"),"sutroon");
		/// </summary>
		/// <param name="str"></param>
		/// <param name="key"></param>
		/// <returns></returns>
		/// <remarks>2014-1-5 sutroon</remarks>
		public static string EncryptTextForFile(string str, string key)
        {
            if (string.IsNullOrEmpty(str)) return str;
            byte[] bStr = (new UnicodeEncoding()).GetBytes(str);
            byte[] bKey = (new UnicodeEncoding()).GetBytes(key);
            for (int i = 0; i < bStr.Length; i += 2)
            {
                for (int j = 0; j < bKey.Length; j += 2)
                {
                    bStr[i] = Convert.ToByte(bStr[i] ^ bKey[j]);
                }
            }
            return (new UnicodeEncoding()).GetString(bStr).TrimEnd('/', '0');
        }
    }
}
